#include <iostream>
#include <cstdio>
#include <queue>
#include <algorithm>

struct edge { int from, to, cost; };

bool operator<(edge const & a, edge const & b)
{
	return a.cost > b.cost;
}

int const inf =  1 << 29;
int const maxn = 505;
int const maxm = 50010;
bool vis[maxn];
edge edges[maxm];
int map[maxn][maxn];
int max_edge[maxn][maxn];
bool span_tree[maxn][maxn];
int n, m;

int next[2 * maxn], head[maxn], end_point[2 * maxn];
int alloc = 1;

bool useless[maxn][maxn];

void init()
{
	for (int i = 1; i <= n; i++)
		for (int j = i + 1; j <= n; j++) map[i][j] = map[j][i] = inf;
}

void add_edge(int u, int v, int c)
{
	//    std::cout << "---> span tree:  " << u << ' ' << v << ' ' << c << '\n';
	end_point[alloc] = v; next[alloc] = head[u]; head[u] = alloc++;
	end_point[alloc] = u; next[alloc] = head[v]; head[v] = alloc++;
}

bool dfs(int u, int father, int cost, int target, int max)
{
	if (u == target) {
		if (cost == max) useless[u][father] = useless[father][u] = true;
		return true;
	}
	for (int p = head[u]; p; p = next[p]) {
		int v = end_point[p];
		if (v == father) continue;
		if (dfs(v, u, map[u][v], target, max)) {
			if (cost == max) useless[u][father] = useless[father][u] = true;
			return true;
		}
	}
	return false;
}

int main()
{
	std::scanf("%d %d", &n, &m);
	init();
	for (int i = 0, x, y, z; i < m; i++) {
		std::scanf("%d %d %d", &x, &y, &z);
		edges[i].from = x; edges[i].to = y; edges[i].cost = z;
		map[x][y] = map[y][x] = std::min(map[x][y], z);
	}

	std::priority_queue<edge> pq;
	vis[1] = true;
	for (int i = 2; i <= n; i++) {
		if (map[1][i] >= inf) continue;
		edge tmp;
		tmp.from = 1; tmp.to = i; tmp.cost = map[i][1];
		pq.push(tmp);
	}
	for (int i = 1; i < n; i++) {
		while (!pq.empty() && vis[pq.top().to]) pq.pop();
		if (pq.empty()) continue;
		edge now = pq.top(); pq.pop();
		vis[now.to] = true;
		add_edge(now.from, now.to, now.cost);
		span_tree[now.from][now.to] = span_tree[now.to][now.from] = true;
		for (int i = 2; i <= n; i++) {
			if (map[now.to][i] >= inf || i == now.to) continue;
			if (!vis[i]) {
				edge tmp;
				tmp.from = now.to;  tmp.to = i;  tmp.cost = map[now.to][i];
				pq.push(tmp);
			} else {
				max_edge[i][now.to] = max_edge[now.to][i] = std::max(max_edge[now.from][i], now.cost);
			}
		}
	}


	for (int i = 0; i < m; i++) {
		if (span_tree[edges[i].from][edges[i].to]) continue;
		if (edges[i].cost > max_edge[edges[i].from][edges[i].to]) continue;
		//        std::cout << "--> check: " << edges[i].from << ' ' << edges[i].to << '\n';
		dfs(edges[i].from, -1, -1, edges[i].to, edges[i].cost);
		useless[edges[i].from][edges[i].to] = useless[edges[i].to][edges[i].from] = true;
	}

	int count = 0, ans = 0;
	for (int i = 1; i <= n; i++)
		for (int j = i + 1; j <= n; j++) {
			if (!span_tree[i][j]) continue;
			if (map[i][j] >= inf || useless[i][j]) continue;
			//            std::cout << "--->" << i << ' ' << j << ' ' << map[i][j] << '\n';
			count++; ans += map[i][j];
		}

	std::printf("%d %d\n", count, ans);
}

